/*
 * The class makes exercises for equivalence of FAs and REs
 */

package cz.muni.fi.xkadlec6.bp;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import cz.muni.fi.xkadlec6.bp.automaton.Automaton;
import cz.muni.fi.xkadlec6.bp.automaton.AutomatonGenerator;
import cz.muni.fi.xkadlec6.bp.automaton.AutomatonModificator;
import cz.muni.fi.xkadlec6.bp.regularExpression.RegularExpression;
import cz.muni.fi.xkadlec6.bp.regularExpression.RegularExpressionConverter;

/**
 * The class T3Generator stands for the backgroud thread, which generates the exercises for equivalence of finite
 * automatons and regular expressions
 * 
 * @author Jana Kadlecova
 */
public class T3Generator extends SwingWorker<Integer, Integer>
{

	// general atributes
	private ExerciseGeneratorGUI frame;
	private Map<String, JTextArea> output;
	private int exercises;
	private int k; // number of ready exercises
	private int operation; // 0 or 1

	// attributes for operation 0, 1
	private int numberOfStatesMin;
	private int numberOfStatesMax;
	private int numberOfFinalStatesMin;
	private int numberOfFinalStatesMax;
	private int numberOfTransitionsMin;
	private int numberOfTransitionsMax;
	private int sizeOfAlphabetMin;
	private int sizeOfAlphabetMax;
	private int alphabet;
	private int states;

	// attributes for operation 0
	private int resultLengthMin;// optional (if is not used, is -1)
	private int resultLengthMax;// optional (if is not used, is -1)
	private int resultSizeOfAlphabetMin; // optional (if is not used, is -1)
	private int resultSizeOfAlphabetMax; // optional (if is not used, is -1)

	// attributes for operation 1
	private int resultNumberOfStatesMin;
	private int resultNumberOfStatesMax;
	private int resultNumberOfTransitionsMin;
	private int resultNumberOfTransitionsMax;
	// optional attributes for operation 1
	private int lengthMin;// optional (if is not used, is -1)
	private int lengthMax;// optional (if is not used, is -1)

	/**
	 * Constructor, which makes new backgroud thread. The thread generates the exercises for converting DFA -> regular
	 * expression
	 * 
	 * @param frame
	 *            ExerciseGeneratorGUI
	 * @param output
	 *            JTextAreas, where should be the result printed
	 * @param exercises
	 *            number of exercises
	 * @param alphabet
	 *            type of the alphabet: 0 -- a, b, c (lower case); 1 -- A, B, C (upper case); 2 -- 1, 2, 3 (numbers); 3
	 *            -- x, y, z (lower case end of rhe alphabet); 4 -- X, Y, Z (upper case end of the alphabet); 5 -- t0,
	 *            t1, t2 (letter 't' with index); 6 -- p0, p1, p2 (letter 'p' with index); 7 -- I, II, III, IV (roman
	 *            numbers); 8 -- i, ii, iii, iv (roman numbers lower case); 9 -- 1, 10, 11, 100 (binary numbers)
	 * @param states
	 *            type of the states: 0 -- a, b, c (lower case); 1 -- A, B, C (upper case); 2 -- 1, 2, 3 (numbers); 3 --
	 *            x, y, z (lower case end of rhe alphabet); 4 -- X, Y, Z (upper case end of the alphabet); 5 -- q0, q1,
	 *            q2 (letter 'q' with index); 6 -- s0, s1, s2 (letter 's' with index); 7 -- I, II, III, IV (roman
	 *            numbers); 8 -- i, ii, iii, iv (roman numbers lower case); 9 -- 1, 10, 11, 100 (binary numbers)
	 * @param sizeOfAlphabetMin
	 *            size of alphabet min
	 * @param sizeOfAlphabetMax
	 *            size of alphabet max
	 * @param numberOfStatesMin
	 *            number of states min
	 * @param numberOfStatesMax
	 *            number of states max
	 * @param numberOfFinalStatesMin
	 *            number of final states min
	 * @param numberOfFinalStatesMax
	 *            number of final states max
	 * @param numberOfTransitionsMin
	 *            number of transitions min
	 * @param numberOfTransitionsMax
	 *            number of transitions max
	 * @param resultSizeOfAlphabetMin
	 *            resulting RE: size of alphabet min
	 * @param resultSizeOfAlphabetMax
	 *            resulting RE:size of alphabet max
	 * @param resultLengthMin
	 *            resulting RE: length of the regular expression min
	 * @param resultLengthMax
	 *            resulting RE: length of the regular expression min
	 */
	public T3Generator(ExerciseGeneratorGUI frame, Map<String, JTextArea> output, int exercises, int alphabet,
		int states, int sizeOfAlphabetMin, int sizeOfAlphabetMax, int numberOfStatesMin, int numberOfStatesMax,
		int numberOfFinalStatesMin, int numberOfFinalStatesMax, int numberOfTransitionsMin, int numberOfTransitionsMax,
		int resultSizeOfAlphabetMin, int resultSizeOfAlphabetMax, int resultLengthMin, int resultLengthMax)
	{

		this.operation = 0;
		this.output = output;
		this.exercises = exercises;
		this.frame = frame;
		this.k = 0;

		this.numberOfStatesMin = numberOfStatesMin;
		this.numberOfStatesMax = numberOfStatesMax;
		this.numberOfFinalStatesMin = numberOfFinalStatesMin;
		this.numberOfFinalStatesMax = numberOfFinalStatesMax;
		this.numberOfTransitionsMin = numberOfTransitionsMin;
		this.numberOfTransitionsMax = numberOfTransitionsMax;
		this.sizeOfAlphabetMin = sizeOfAlphabetMin;
		this.sizeOfAlphabetMax = sizeOfAlphabetMax;
		this.alphabet = alphabet;
		this.states = states;
		this.resultSizeOfAlphabetMin = resultSizeOfAlphabetMin;
		this.resultSizeOfAlphabetMax = resultSizeOfAlphabetMax;
		this.resultLengthMin = resultLengthMin;
		this.resultLengthMax = resultLengthMax;

	}

	/**
	 * Constructor, which makes new backgroud thread. The thread generates the exercises for converting regular
	 * expression -> NFA with epsilon-transitions
	 * 
	 * @param frame
	 *            ExerciseGeneratorGUI
	 * @param output
	 *            JTextAreas, where should be the result printed
	 * @param exercises
	 *            number of exercises
	 * @param alphabet
	 *            type of the alphabet: 0 -- a, b, c (lower case); 1 -- A, B, C (upper case); 2 -- 1, 2, 3 (numbers); 3
	 *            -- x, y, z (lower case end of rhe alphabet); 4 -- X, Y, Z (upper case end of the alphabet); 5 -- t0,
	 *            t1, t2 (letter 't' with index); 6 -- p0, p1, p2 (letter 'p' with index); 7 -- I, II, III, IV (roman
	 *            numbers); 8 -- i, ii, iii, iv (roman numbers lower case); 9 -- 1, 10, 11, 100 (binary numbers)
	 * @param states
	 *            type of the states: 0 -- a, b, c (lower case); 1 -- A, B, C (upper case); 2 -- 1, 2, 3 (numbers); 3 --
	 *            x, y, z (lower case end of rhe alphabet); 4 -- X, Y, Z (upper case end of the alphabet); 5 -- q0, q1,
	 *            q2 (letter 'q' with index); 6 -- s0, s1, s2 (letter 's' with index); 7 -- I, II, III, IV (roman
	 *            numbers); 8 -- i, ii, iii, iv (roman numbers lower case); 9 -- 1, 10, 11, 100 (binary numbers)
	 * @param sizeOfAlphabetMin
	 *            size of alphabet min
	 * @param sizeOfAlphabetMax
	 *            size of alphabet max
	 * @param lengthMin
	 *            length of the regular expression min
	 * @param lengthMax
	 *            length of the regular expression max
	 * @param resultNumberOfStatesMin
	 *            resulting automaton: number of states min
	 * @param resultNumberOfStatesMax
	 *            resulting automaton: number of states max
	 * @param resultNumberOfTransitionsMin
	 *            resulting automaton: number of transitions min
	 * @param resultNumberOfTransitionsMax
	 *            resulting automaton: number of transitions max
	 */
	public T3Generator(ExerciseGeneratorGUI frame, Map<String, JTextArea> output, int exercises, int alphabet,
		int states, int sizeOfAlphabetMin, int sizeOfAlphabetMax, int lengthMin, int lengthMax,
		int resultNumberOfStatesMin, int resultNumberOfStatesMax, int resultNumberOfTransitionsMin,
		int resultNumberOfTransitionsMax)
	{

		this.operation = 1;
		this.output = output;
		this.exercises = exercises;
		this.frame = frame;
		this.k = 0;

		this.sizeOfAlphabetMin = sizeOfAlphabetMin;
		this.sizeOfAlphabetMax = sizeOfAlphabetMax;
		this.lengthMin = lengthMin; // -1, if is not set
		this.lengthMax = lengthMax; // -1, if is not set
		this.resultNumberOfStatesMin = resultNumberOfStatesMin;
		this.resultNumberOfStatesMax = resultNumberOfStatesMax;
		this.resultNumberOfTransitionsMin = resultNumberOfTransitionsMin;
		this.resultNumberOfTransitionsMax = resultNumberOfTransitionsMax;
		this.alphabet = alphabet;
		this.states = states;
		this.numberOfStatesMin = resultNumberOfStatesMin;
		this.numberOfStatesMax = resultNumberOfStatesMax;
		this.numberOfTransitionsMin = resultNumberOfTransitionsMin;
		this.numberOfTransitionsMax = resultNumberOfTransitionsMax;
		this.numberOfFinalStatesMin = 1;
		this.numberOfFinalStatesMax = 3;
	}

	@Override
	protected Integer doInBackground() throws Exception
	{
		boolean enough = false;
		Map<String, String> result = new HashMap<String, String>();
		// StringBulder for each kind of result form
		StringBuilder sbLatex = new StringBuilder();
		StringBuilder sbPlainText = new StringBuilder();
		StringBuilder sbIS = new StringBuilder();
		StringBuilder sbLatexEN = new StringBuilder();
		StringBuilder sbPlainTextEN = new StringBuilder();
		StringBuilder sbISEN = new StringBuilder();
		// the beginnig of some kinds of exercises is set
		sbLatex.append("\\documentclass{article}\n\\usepackage{czech}\n"
			+ "\\usepackage[utf8]{inputenc}\n\\begin{document}\n");
		sbLatexEN.append("\\documentclass{article}\n\\begin{document}\n");
		sbIS.append("\n");
		sbISEN.append("\n");
		// the task is set
		Map<String, String> task = setTask();
		// calculate
		while (!isCancelled() && !enough)
		{
			Automaton a = null;
			// generates automaton with the given parameters
			do
			{
				a = callFunctionGenerateAutomaton();
				if (a == null && k == 0)
				{
					this.frame.resetStopGenerateButtons();
					return null;
				}
				if (a == null)
				{
					break;
				}
			}
			while (!isSuitable(a) && !this.isCancelled());
			if (!this.isCancelled())
			{
				// divide exercises
				// _______________ Exercise (number of exercise)____________________
				String line = "________________________________________";
				String czech = line + "Příklad " + (k + 1) + line;
				sbLatex.append("\n%" + czech + "\n\\section*{Příklad " + (k + 1) + "}");
				sbPlainText.append(czech + "\n\n");
				sbIS.append("<!--" + czech + "-->\n");

				String english = line + "Exercise " + (k + 1) + line;
				sbLatexEN.append("\n%" + english + "\n\\section*{Exercise " + (k + 1) + "}");
				sbPlainTextEN.append(english + "\n\n");
				sbISEN.append("<!--" + english + "-->\n");
				// sets the task
				sbLatex.append(task.get("latex"));
				sbPlainText.append(task.get("plainText"));
				sbIS.append(task.get("IS"));

				sbLatexEN.append(task.get("latexEN"));
				sbPlainTextEN.append(task.get("plainTextEN"));
				sbISEN.append(task.get("ISEN"));
				// changes the names of the states
				AutomatonModificator.modifyAlphabet(a, alphabet);
				AutomatonModificator.modifyStates(a, states);
				// Automaton and the row with text "Result"
				if (operation == 0)
				{
					sbLatex.append(a.toLaTeX() + "\n\n\\subsection*{Řešení}\n");
					sbLatexEN.append(a.toLaTeX() + "\n\n\\subsection*{Result}\n");
					sbPlainText.append(a.toString().replace("transition function", "přechodová funkce")
						+ "\n\nŘešení:\n\n");
					sbPlainTextEN.append(a.toString() + "\n\nResult:\n\n");
					sbLatexEN.append("\\noindent\nThe regular expression:\n\n");
					sbPlainTextEN.append("The regular expression:\n");
					sbLatex.append("\\noindent\nRegulární výraz:\n\n");
					sbPlainText.append("Regulární výraz:\n");
					sbIS.append("<M>" + a.toLaTeX().replace("$", "") + "</M>\n");
					sbISEN.append("<M>" + a.toLaTeX().replace("$", "") + "</M>\n");
				}
				else
				{
					RegularExpression re = RegularExpressionConverter.convertAutomatonToRegularExpression(a);
					sbLatex.append(re.toLaTeX() + "\n\n\\subsection*{Řešení}\n");
					sbLatexEN.append(re.toLaTeX() + "\n\n\\subsection*{Result}\n");
					sbPlainText.append(re.toString() + "\n\nŘešení:\n\n");
					sbPlainTextEN.append(re.toString() + "\n\nResult:\n\n");
					sbLatexEN.append("\\noindent\nThe NFA (may contain $\\varepsilon$-transitions):\n\n");
					sbPlainTextEN.append("The NFA (may contain epsilon-transitions):\n");
					sbLatex.append("\\noindent\nNFA (může obsahovat $\\varepsilon$-kroky):\n\n");
					sbPlainText.append("NFA (může obsahovat epsilon-kroky):\n");
					sbIS.append("<M>" + re.toLaTeX().replace("$$", "") + "</M>\n");
					sbISEN.append("<M>" + re.toLaTeX().replace("$$", "") + "</M>\n");
				}
				// result
				Map<String, String> generatedResult = generateResults(a);
				sbLatexEN.append(generatedResult.get("latexEN") + "\n\n");
				sbPlainText.append(generatedResult.get("plainText") + "\n\n");
				sbIS.append(generatedResult.get("IS"));
				sbLatex.append(generatedResult.get("latex") + "\n\n");
				sbPlainTextEN.append(generatedResult.get("plainTextEN") + "\n\n");
				sbISEN.append(generatedResult.get("ISEN"));
				if (k != exercises - 1)
				{
					sbIS.append("--\n");
					sbISEN.append("--\n");
				}
				if (k == exercises - 1)
				{
					enough = true;
				}
				k++;
				this.publish(Integer.valueOf(k));
			}
		}
		// the end of some kinds of exercises is set
		sbLatex.append("\n\\end{document}\n");
		sbLatexEN.append("\n\\end{document}\n");
		// ready exercises are put into the map result
		result.put("plainText", sbPlainText.toString());
		result.put("latex", sbLatex.toString());
		result.put("IS", sbIS.toString());
		result.put("plainTextEN", sbPlainTextEN.toString());
		result.put("latexEN", sbLatexEN.toString());
		result.put("ISEN", sbISEN.toString());
		for (String s : output.keySet())
		{
			output.get(s).setText(result.get(s) + "\n");
		}
		return null;
	}

	@Override
	protected void process(List<Integer> pairs)
	{
		this.frame.setProgress(pairs.get(pairs.size() - 1));

	}

	private Automaton callFunctionGenerateAutomaton()
	{
		Automaton a = null;
		do
		{
			try
			{
				a = AutomatonGenerator.generateAutomaton(numberOfStatesMin, numberOfStatesMax, numberOfFinalStatesMin,
					numberOfFinalStatesMax, numberOfTransitionsMin, numberOfTransitionsMax, sizeOfAlphabetMin,
					sizeOfAlphabetMax, 0, numberOfStatesMax - 1, true, false, false, this);
				if (a == null)
				{
					return a;
				}
			}
			catch (Exception ex)
			{
				javax.swing.JOptionPane.showMessageDialog(new JFrame(), ex.getMessage(), "Warning",
					JOptionPane.WARNING_MESSAGE);
				return null;
			}
		}
		while ((!(a.getStates().size() >= numberOfStatesMin) || !(a.getStates().size() <= numberOfStatesMax)
			|| !(a.getNumberOfTransitions() >= numberOfTransitionsMin)
			|| !(a.getNumberOfTransitions() <= numberOfTransitionsMax)
			|| !(a.getAlphabet().size() >= sizeOfAlphabetMin) || !(a.getAlphabet().size() <= sizeOfAlphabetMax))
			&& (!this.isCancelled()));
		if (this.isCancelled())
		{
			return null;
		}
		return a;
	}

	private Map<String, String> generateResults(Automaton automaton)
	{
		StringBuilder sbLatex = new StringBuilder();
		StringBuilder sbLatexEN = new StringBuilder();
		StringBuilder sbPlainText = new StringBuilder();
		StringBuilder sbPlainTextEN = new StringBuilder();
		StringBuilder sbIS = new StringBuilder();
		Automaton a = new Automaton(automaton);
		RegularExpression re = null;
		sbIS.append("\n");
		// DFA -> regular expression
		if (operation == 0)
		{
			re = RegularExpressionConverter.convertAutomatonToRegularExpression(a);
			sbPlainText.append(re.toString() + "\n");
			sbLatex.append(re.toLaTeX() + "\n");
			sbIS.append(a.toIS("DFA", "REG").replace("$", ""));
			sbPlainTextEN.append(re.toString() + "\n");
			sbLatexEN.append(re.toLaTeX() + "\n");

		}
		// regular expression -> NFA with epsilon-transitions
		else
		{
			// provest operaci
			re = RegularExpressionConverter.convertAutomatonToRegularExpression(a);
			Automaton b = RegularExpressionConverter.convertRegularExpressionToAutomaton(re);
			AutomatonModificator.modifyStates(b, this.states);
			sbLatexEN.append(b.toLaTeX() + "\n");
			sbPlainTextEN.append(b.toString() + "\n");
			sbLatex.append(b.toLaTeX() + "\n");
			sbPlainText.append(b.toString() + "\n");
			if (b.containsEpsilonTransitions())
			{
				sbIS.append(b.toIS("EFA", "EFA"));
			}
			else
			{
				sbIS.append(b.toIS("NFA", "NFA"));
			}

		}
		Map<String, String> result = new HashMap<String, String>();
		result.put("latex", sbLatex.toString());
		result.put("plainText", sbPlainText.toString());
		result.put("IS", sbIS.toString());
		result.put("latexEN", sbLatexEN.toString());
		result.put("plainTextEN", sbPlainTextEN.toString());
		result.put("ISEN", sbIS.toString());
		return result;
	}

	private boolean isSuitable(Automaton automaton)
	{
		Automaton a = new Automaton(automaton);

		RegularExpression re = RegularExpressionConverter.convertAutomatonToRegularExpression(a);

		// podminky doplnit
		if (operation == 1)
		{
			return this.isSuitable(re);
		}

		int i = re.getAlphabet().size();
		if (resultSizeOfAlphabetMin > -1 && resultSizeOfAlphabetMax > -1)
		{
			if (i > resultSizeOfAlphabetMax || i < resultSizeOfAlphabetMin)
			{
				return false;
			}
		}
		i = re.getLength();
		if (resultLengthMax > -1 && resultLengthMin > -1)
		{
			if (i > resultLengthMax || i < resultLengthMin)
			{
				return false;
			}
		}

		return true;
	}

	private boolean isSuitable(RegularExpression re)
	{
		int i = re.getAlphabet().size();
		if (i > sizeOfAlphabetMax || i < sizeOfAlphabetMin)
		{
			return false;
		}
		Automaton b = RegularExpressionConverter.convertRegularExpressionToAutomaton(re);
		if (b.isDeterministic())
		{
			return false;
		}
		if (lengthMin > -1 && lengthMax > -1)
		{
			i = re.getLength();
			if (i < lengthMin || i > lengthMax)
			{
				return false;
			}
		}
		i = b.getStates().size();
		if (i > resultNumberOfStatesMax || i < resultNumberOfStatesMin)
		{
			return false;
		}
		i = b.getNumberOfTransitions();
		if (i > resultNumberOfTransitionsMax || i < resultNumberOfTransitionsMin)
		{
			return false;
		}
		return true;
	}

	private Map<String, String> setTask()
	{
		Map<String, String> result = new HashMap<String, String>();
		StringBuilder sbLatex = new StringBuilder();
		StringBuilder sbLatexEN = new StringBuilder();
		StringBuilder sbPlainText = new StringBuilder();
		StringBuilder sbPlainTextEN = new StringBuilder();
		StringBuilder sbIS = new StringBuilder();
		StringBuilder sbISEN = new StringBuilder();
		String czech = "Najděte jazykově ekvivalentní ";
		String english = "Find a language equivalent ";
		String english2 = "";
		String czech2 = "";
		switch (operation)
		{
			case 0:
				czech2 = "regulární výraz pro deterministický konečný automat, který je zadaný následující tabulkou:";
				english2 = "regular expression to the deterministic finite automaton given with the table:";
				break;
			case 1:
				czech2 = "NFA (může obsahovat epsilon-kroky) k zadanému regulárnímu výrazu:";
				english2 = "NFA (may contain epsilon-transitions) to the given regular expression:";
				break;
		}
		sbPlainText.append(czech + czech2 + "\n\n");
		sbLatex.append("\n\\noindent\n" + czech + czech2.replace("epsilon", "$\\varepsilon$") + "\n\n");
		sbIS.append("<p>" + czech + czech2.replace("epsilon", "<M>\\varepsilon</M>") + "</p>\n\n");
		sbPlainTextEN.append(english + english2 + "\n\n");
		sbLatexEN.append("\n\\noindent\n" + english + english2.replace("epsilon", "$\\varepsilon$") + "\n\n");
		sbISEN.append("<p>" + english + english2.replace("epsilon", "<M>\\varepsilon</M>") + "</p>\n\n");
		result.put("latex", sbLatex.toString());
		result.put("latexEN", sbLatexEN.toString());
		result.put("plainText", sbPlainText.toString());
		result.put("plainTextEN", sbPlainTextEN.toString());
		result.put("IS", sbIS.toString());
		result.put("ISEN", sbISEN.toString());
		return result;
	}

}
